home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / vms_mail.c < prev    next >
C/C++ Source or Header  |  1994-10-06  |  66KB  |  2,325 lines

  1. /*
  2.  * Program:    VMS mail routines
  3.  *
  4.  * Author:    Yehavi Bourvine, The Hebrew University of Jerusalem.
  5.  *        Internet: Yehavi@VMS.huji.ac.il
  6.  *
  7.  * Date:    2 August 1994
  8.  * Last Edited:    6 October 1994
  9.  *
  10.  * Copyright 1993 by the University of Washington
  11.  *
  12.  *  Permission to use, copy, modify, and distribute this software and its
  13.  * documentation for any purpose and without fee is hereby granted, provided
  14.  * that the above copyright notice appears in all copies and that both the
  15.  * above copyright notice and this permission notice appear in supporting
  16.  * documentation, and that the name of the University of Washington not be
  17.  * used in advertising or publicity pertaining to distribution of the software
  18.  * without specific, written prior permission.  This software is made
  19.  * available "as is", and
  20.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  21.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  22.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  23.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  24.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  25.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  26.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  27.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  28.  *
  29.  */
  30.  
  31. #include <stdio.h>
  32. #include <errno.h>
  33. extern int errno;        /* just in case */
  34. #include <signal.h>
  35. /* #include <sys/time.h>        /* must be before osdep.h */
  36. #include "mail.h"
  37. #include "osdep.h"
  38. #include <sys/file.h>
  39. #include <sys/stat.h>
  40. #include "vms_mail.h"
  41. #include "rfc822.h"
  42. #include "misc.h"
  43. #include "dummy.h"
  44.  
  45. /* VMS/mail routines */
  46.  
  47. /* Context values used by the MAIL$ routines. Since we opened the mail file and
  48.    we can open it only once we save it in a static variable. This way we also
  49.    know to close it when a folder is switched.
  50.    we also keep the number of messages when the folder was opened. This is
  51.    needed since PINE checks this number once in a while; if new message arrive
  52.    it will fool our cache mechanism.
  53. */
  54. static    int    MailContext = 0,    /* Points to the opened VMS/MAIL file */
  55.         MessageContext = 0;    /* To the currently opened folder */
  56.         TotalMessagesCount = -1;    /* How many messages in this folder? */
  57.  
  58. static char    GlobalMailDirectory[256];    /* See description in ___vms_mail_open() */
  59.  
  60. /* List of folders so we won't open a folder that does not exist */
  61. #define    MAX_FOLDERS    256
  62. char    FoldersNames[MAX_FOLDERS][64];
  63. int    FoldersNamesCount;
  64.  
  65. /* Driver dispatch used by MAIL */
  66.  
  67. DRIVER vmsmaildriver = {
  68.   "vms_mail",            /* driver name */
  69.   (DRIVER *) NIL,        /* next driver */
  70.   vms_mail_valid,            /* mailbox is valid for us */
  71.   vms_mail_parameters,        /* manipulate parameters */
  72.   vms_mail_find,            /* find mailboxes */
  73.   vms_mail_find_bboards,        /* find bboards */
  74.   vms_mail_find_all,        /* find all mailboxes */
  75.   vms_mail_find_all_bboards,    /* find all bboards */
  76.   vms_mail_subscribe,        /* subscribe to mailbox */
  77.   vms_mail_unsubscribe,        /* unsubscribe from mailbox */
  78.   vms_mail_subscribe_bboard,    /* subscribe to bboard */
  79.   vms_mail_unsubscribe_bboard,    /* unsubscribe from bboard */
  80.   vms_mail_create,            /* create mailbox */
  81.   vms_mail_delete,            /* delete mailbox */
  82.   vms_mail_rename,            /* rename mailbox */
  83.   vms_mail_open,            /* open mailbox */
  84.   vms_mail_close,            /* close mailbox */
  85.   vms_mail_fetchfast,        /* fetch message "fast" attributes */
  86.   vms_mail_fetchflags,        /* fetch message flags */
  87.   vms_mail_fetchstructure,        /* fetch message envelopes */
  88.   vms_mail_fetchheader,        /* fetch message header only */
  89.   vms_mail_fetchtext,        /* fetch message body only */
  90.   vms_mail_fetchbody,        /* fetch message body section */
  91.   vms_mail_setflag,        /* set message flag */
  92.   vms_mail_clearflag,        /* clear message flag */
  93.   vms_mail_search,            /* search for message based on criteria */
  94.   vms_mail_ping,            /* ping mailbox to see if still alive */
  95.   vms_mail_check,            /* check for new messages */
  96.   vms_mail_expunge,        /* expunge deleted messages */
  97.   vms_mail_copy,            /* copy messages to another mailbox */
  98.   vms_mail_move,            /* move messages to another mailbox */
  99.   vms_mail_append,            /* append string message to mailbox */
  100.   vms_mail_gc            /* garbage collect stream */
  101. };
  102.  
  103.                 /* prototype stream */
  104. MAILSTREAM vmsmailproto = {&vmsmaildriver};
  105.                                 /* standard driver's prototype */
  106. MAILSTREAM *mailstd_proto = &vmsmailproto;
  107.  
  108. /* VMS/MAIL mail validate mailbox
  109.  * Accepts: mailbox name
  110.  * Returns: our driver if name is valid, NIL otherwise
  111.  */
  112.  
  113. DRIVER *vms_mail_valid (name)
  114.     char *name;
  115. {
  116.   char tmp[MAILTMPLEN];
  117.   return vms_mail_isvalid (name,tmp) ? &vmsmaildriver : NIL;
  118. }
  119.  
  120.  
  121. /* VMS/MAIL mail test for valid mailbox
  122.  * Accepts: mailbox name
  123.  * Returns: T if valid, NIL otherwise
  124.  */
  125.  
  126. long vms_mail_isvalid (name,tmp)
  127.     char *name;
  128.     char *tmp;
  129. {
  130. #ifdef MULTINET
  131.   struct hostent *host_name;
  132.  
  133.     gethostname(tmp,MAILTMPLEN);/* get local host name */
  134. #else    /* MULTINET */
  135.     strncpy(tmp, getenv("SYS$NODE"), MAILTMPLEN);
  136. #endif    /* Multinet */
  137.   return 1;            /* Always succeed */
  138. }
  139.  
  140.  
  141. /* VMS/MAIL manipulate driver parameters
  142.  * Accepts: function code
  143.  *        function-dependent value
  144.  * Returns: function-dependent return value
  145.  */
  146.  
  147. void *vms_mail_parameters (function,value)
  148.     long function;
  149.     void *value;
  150. {
  151.   return NIL;
  152. }
  153.  
  154. /* VMS/MAIL mail find list of mailboxes
  155.  * Accepts: mail stream
  156.  *        pattern to search
  157.  */
  158.  
  159. void vms_mail_find (stream,pat)
  160.     MAILSTREAM *stream;
  161.     char *pat;
  162. {
  163.     vms_mail_find_all(stream, pat);    /* find all folders */
  164. }
  165.  
  166.  
  167. /* VMS/MAIL mail find list of bboards
  168.  * Accepts: mail stream
  169.  *        pattern to search
  170.  */
  171.  
  172. void vms_mail_find_bboards (stream,pat)
  173.     MAILSTREAM *stream;
  174.     char *pat;
  175. {
  176.   return;        /* no bboards supported on VMS */
  177. }
  178.  
  179.  
  180. /* VMS/MAIL mail find list of all mailboxes - in VMS'ish it is simply get
  181.  *  the list of all folders.
  182.  * Accepts: mail stream
  183.  *        pattern to search
  184.  */
  185.  
  186. void vms_mail_find_all (stream,pat)
  187.     MAILSTREAM *stream;
  188.     char *pat;
  189. {
  190.     int    status;
  191.     char    *list_folders();
  192.     struct    ITEM_LIST ListFoldersItem[] = {    /* get a list of all folders */
  193.         { sizeof(char *), MAIL$_MAILFILE_FOLDER_ROUTINE,
  194.             (char *)list_folders, NULL },
  195.         { 0, 0, NULL, NULL } };
  196.     struct    ITEM_LIST NullItemList[] = {{ 0, 0, NULL, NULL }};    /* Null list */
  197.     int    MailOpened = 0;
  198.     char    *p, *MailDirectory;
  199.  
  200. /* Check whether this is the postponed or interrupted mail. If so, check for
  201.    the file. Must be synchronised with pine/OS_VMS.H
  202. */
  203.   if((strstr(pat, "POSTPONED-MAIL") != NULL) ||
  204.      (strstr(pat, "INTERRUPTED-MAIL") != NULL)) {
  205.     FILE    *ifd;
  206.     if((ifd = fopen(pat, "r")) != NULL) {
  207.         mm_mailbox(pat);        /* Register it */
  208.         fclose(ifd);
  209.     }
  210.     return;
  211.   }
  212.  
  213.   if((p = strchr(pat, '(')) != NULL) {
  214.     MailDirectory = malloc(strlen(p));
  215.     strcpy(MailDirectory, ++p);
  216.     if((p = strchr(MailDirectory, ')')) != NULL) *p = '\0';
  217.   }
  218.   else MailDirectory = NULL;
  219.  
  220. /* We might be called here at the middle of some other operation. If so, do not
  221.    re-open and re-close the file. */
  222.     if(MailContext == 0) {    /* Not opened yet */
  223.         ___vms_mail_open(MailDirectory);    /* open the mail file */
  224.         MailOpened = 1;
  225.     }
  226.     status = mail$mailfile_info_file(&MailContext, ListFoldersItem, NullItemList);
  227.     if((status & 0x1) == 0) {
  228.         printf(" Info failed, status=%d\n", status);
  229.         exit(status);
  230.     }
  231.     if(MailOpened)    /* We opened it, so we have to close it */
  232.         ___vms_mail_close();    /* Close it */
  233. }
  234.  
  235.  
  236. /* this routine is called for each folder by the INFO routine. It reports
  237.    the name of the folder back to PINE.
  238. */
  239. char *
  240. list_folders(UserData, FolderName)
  241. int    *UserData;
  242. struct    DESC    *FolderName;
  243. {
  244.     char    folder[256];
  245.  
  246.     if(FolderName == NULL) return;    /* Shouldn't happen */
  247.     if(FolderName->length == 0) return;    /* End of list */
  248.  
  249.     strncpy(folder, FolderName->address, FolderName->length);
  250.     folder[FolderName->length & 0xffff] = '\0';
  251.     mm_mailbox(folder);        /* Register it */
  252.     return 1;
  253. }
  254.  
  255. /*
  256.  | This routine does much the same as the previous one, but this time
  257.  | it keeps the list here for internal use.
  258.  */
  259. int
  260. vms_mail_list_folders(UserData, FolderName)
  261. int    *UserData;
  262. struct    DESC    *FolderName;
  263. {
  264.     char    folder[256];
  265.  
  266.     if(FolderName == NULL) return;    /* Shouldn't happen */
  267.     if(FolderName->length == 0) return;    /* End of list */
  268.  
  269.     strncpy(FoldersNames[FoldersNamesCount], FolderName->address, FolderName->length);
  270.     FoldersNames[FoldersNamesCount][FolderName->length & 0xffff] = '\0';
  271.     *FoldersNames[++FoldersNamesCount] = '\0';    /* End of list */
  272.     return 1;
  273. }
  274.  
  275.  
  276. /* VMS/MAIL mail find list of all bboards
  277.  * Accepts: mail stream
  278.  *        pattern to search
  279.  */
  280.  
  281. void vms_mail_find_all_bboards (stream,pat)
  282.     MAILSTREAM *stream;
  283.     char *pat;
  284. {
  285.    return;        /* Again, not supported at this stage */
  286. }
  287.  
  288. /* VMS/MAIL mail subscribe to mailbox
  289.  * Accepts: mail stream
  290.  *        mailbox to add to subscription list
  291.  * Returns: T on success, NIL on failure
  292.  */
  293.  
  294. long vms_mail_subscribe (stream,mailbox)
  295.     MAILSTREAM *stream;
  296.     char *mailbox;
  297. {
  298.     return NIL;        /* Only default mailbox on VMS */
  299. }
  300.  
  301.  
  302. /* VMS/MAIL mail unsubscribe to mailbox
  303.  * Accepts: mail stream
  304.  *        mailbox to delete from subscription list
  305.  * Returns: T on success, NIL on failure
  306.  */
  307.  
  308. long vms_mail_unsubscribe (stream,mailbox)
  309.     MAILSTREAM *stream;
  310.     char *mailbox;
  311. {
  312.     return NIL;        /* Only default mailbox on VMS */
  313. }
  314.  
  315.  
  316. /* VMS/MAIL mail subscribe to bboard
  317.  * Accepts: mail stream
  318.  *        bboard to add to subscription list
  319.  * Returns: T on success, NIL on failure
  320.  */
  321.  
  322. long vms_mail_subscribe_bboard (stream,mailbox)
  323.     MAILSTREAM *stream;
  324.     char *mailbox;
  325. {
  326.   return NIL;            /* never valid for VMS/MAIL */
  327. }
  328.  
  329.  
  330. /* VMS/MAIL mail unsubscribe to bboard
  331.  * Accepts: mail stream
  332.  *        bboard to delete from subscription list
  333.  * Returns: T on success, NIL on failure
  334.  */
  335.  
  336. long vms_mail_unsubscribe_bboard (stream,mailbox)
  337.     MAILSTREAM *stream;
  338.     char *mailbox;
  339. {
  340.   return NIL;            /* never valid for VMS/MAIL */
  341. }
  342.  
  343. /* VMS/MAIL mail create mailbox
  344.  * Accepts: MAIL stream
  345.  *        mailbox name to create
  346.  * Returns: T on success, NIL on failure
  347.  */
  348.  
  349. long vms_mail_create (stream,mailbox)
  350.     MAILSTREAM *stream;
  351.     char *mailbox;
  352. {
  353.    mm_log("Create mailbox is not supported on VMS", NULL);
  354.     return NULL;        /* We use only the default, remember? */
  355. }
  356.  
  357.  
  358. /* VMS/MAIL mail delete mailbox
  359.  * Accepts: MAIL stream
  360.  *        mailbox name to delete
  361.  * Returns: T on success, NIL on failure
  362.  */
  363.  
  364. long vms_mail_delete (stream,mailbox)
  365.     MAILSTREAM *stream;
  366.     char *mailbox;
  367. {
  368.    mm_log("Delete mailbox is not supported on VMS", NULL);
  369.   return NIL;
  370. }
  371.  
  372. /* VMS/MAIL mail rename mailbox
  373.  * Accepts: MAIL stream
  374.  *        old mailbox name
  375.  *        new mailbox name (or NIL for delete)
  376.  * Returns: T on success, NIL on failure
  377.  */
  378.  
  379. long vms_mail_rename (stream,old,new)
  380.     MAILSTREAM *stream;
  381.     char *old;
  382.     char *new;
  383. {
  384.    mm_log("Rename mailbox is not supported on VMS", NULL);
  385.     return NIL;
  386. }
  387.  
  388. /* VMS specific mail file open. The context is saved in MailContext which is
  389.  * a global variable.
  390.  */
  391. ___vms_mail_open(MailDirectory)
  392. char    *MailDirectory;
  393. {
  394.     int    status;
  395.     static char    MailFileName[256];
  396.     struct    ITEM_LIST NullItemList[] = {{ 0, 0, NULL, NULL }};    /* Null list */
  397.     struct    ITEM_LIST MailFileList[] = {    /* Set the name of the file */
  398.         { 0, MAIL$_MAILFILE_NAME, MailFileName, NULL }};    /* Will fill values later */
  399.     struct    ITEM_LIST *InItemList;    /* Will be equated later */
  400.     char    *vms_get_mail_directory();
  401.  
  402.     if(MailContext != 0)    /* Already opened... */
  403.         return;
  404.  
  405. /* If we are using the default name then fetch it */
  406.     if(MailDirectory == NULL)
  407.         MailDirectory = vms_get_mail_directory();
  408.  
  409.     status = mail$mailfile_begin(&MailContext, NullItemList, NullItemList);
  410.     if((status & 0x1) == 0) {
  411.         printf(" Begin failed, status=%d\n", status);
  412.         exit(status);
  413.     }
  414.  
  415. /* If the user specified some directory then set it here */
  416.     if(MailDirectory != NULL) {
  417.         sprintf(MailFileName, "%sMAIL.MAI", MailDirectory);
  418.         MailFileList[0].length = strlen(MailDirectory);
  419.         InItemList = MailFileList;
  420.     }
  421.     else    InItemList = NullItemList;
  422.  
  423.     status = mail$mailfile_open(&MailContext, InItemList, NullItemList);
  424.     if((status & 0x1) == 0) {
  425.         printf(" OPen failed, status=%d\n", status);
  426.         exit(status);
  427.     }
  428.  
  429. /* Save the mail directory in a global parameter. This is because MESSAGE_COPY
  430.    which is used to save messages read in MAIL folder or moved by the Save
  431.    command (equivalent of File in VMS/MAIL) uses the login directory; if the
  432.    user set a different mail directory it is not honored unless explicitly
  433.    speicified.
  434. */
  435.     if(MailDirectory != NULL)
  436.         strcpy(GlobalMailDirectory, MailDirectory);
  437.     else    *GlobalMailDirectory = '\0';    /* SHouldn't happen, but... */
  438. }
  439.  
  440. /*
  441.  | Get the full mail directory name of the user. We must do so since MAIL$
  442.  | routines honor it only partially, unless we set the full name at the
  443.  | MAIL_OPEN call.
  444.  */
  445. char *
  446. vms_get_mail_directory()
  447. {
  448.   static int    UserContext, length;
  449.   int        status, mail$user_begin(), mail$user_end(),
  450.         mail$user_get_info();
  451.   static char    FullMailDirectory[256];
  452.   struct    ITEM_LIST MailDirectoryItem[] = {
  453.         { sizeof(FullMailDirectory) - 1, MAIL$_USER_FULL_DIRECTORY,
  454.           FullMailDirectory, (char *)&length },
  455.         { 0, 0, NULL, NULL }};
  456.   struct    ITEM_LIST NullItemList[] = {
  457.         { 0, 0, NULL, NULL }};
  458.  
  459.   UserContext = 0;
  460.   status = mail$user_begin(&UserContext, NullItemList, NullItemList);
  461.   if((status & 0x1) == 0) {
  462.     printf("Mail$user_begin failed with %d\n", status);
  463.     return NULL;
  464.   }
  465.  
  466.   status = mail$user_get_info(&UserContext, NullItemList, MailDirectoryItem);
  467.   if((status & 0x1) == 0) {
  468.     printf("Mail$get_info failed with %d\n", status);
  469.     return NULL;
  470.   }
  471.  
  472.   status = mail$user_end(&UserContext, NullItemList, NullItemList);
  473.   if((status & 0x1) == 0) {
  474.     printf("Mail$user_end failed with %d\n", status);
  475.     return NULL;
  476.   }
  477.  
  478. /* Delimit the name */
  479.    FullMailDirectory[length & 0xff] = '\0';
  480.    return FullMailDirectory;
  481. }
  482.  
  483.  
  484. /* VMS specific mail file close. The context is taken from MailContext which is
  485.  * a global variable.
  486.  */
  487. ___vms_mail_close()
  488. {
  489.     int    status;
  490.     struct    ITEM_LIST CloseItem[] = {    /* Purge and reclaim during close */
  491.         { sizeof(MAIL$_MAILFILE_FULL_CLOSE), MAIL$_MAILFILE_FULL_CLOSE,NULL, NULL, },
  492.         { 0, 0, NULL, NULL } };
  493.     struct    ITEM_LIST NullItemList[] = {{ 0, 0, NULL, NULL }};    /* Null list */
  494.  
  495.     if(MessageContext != 0) {
  496.         status = mail$message_end(&MessageContext, NullItemList, NullItemList);
  497.         if((status & 0x1) == 0) {
  498.             printf(" MessageEnd failed, status=%d\n", status);
  499.         }
  500.     }
  501.  
  502.     status = mail$mailfile_close(&MailContext, CloseItem, NullItemList);
  503.     if((status & 0x1) == 0) {
  504.         mm_log ("Can't close folder",(long) NIL);
  505.         return;
  506.     }
  507.  
  508.     status = mail$mailfile_end(&MailContext, CloseItem, NullItemList);
  509.     if((status & 0x1) == 0) {
  510.         mm_log ("Can't end mail transaction", (long)NIL);
  511.     }
  512.     MailContext = 0;    /* Se we know it is closed */
  513.     TotalMessagesCount = -1;    /* Signal that we do not have value */
  514. }
  515.  
  516. /* VMS/MAIL mail open - open a "mailbox" which is a folder in VMS/MAIL.
  517.  * Accepts: stream to open
  518.  * Returns: stream on success, NIL on failure
  519.  * If the mailbox is in the format (xxx)yyy then we take xxx as the mail
  520.  * directory name and yyy as the mailbox name. This is used for IMAPD.
  521.  */
  522.  
  523. MAILSTREAM *vms_mail_open (stream)
  524.     MAILSTREAM *stream;
  525. {
  526.   long i, status;
  527.   int fd,ld, vms_mail_list_folders();
  528.   char *s,*t,*k;
  529.   char *MailboxName;
  530.   char tmp[MAILTMPLEN];
  531.   struct stat sbuf;
  532.   struct    ITEM_LIST NullItemList[] = {{ 0, 0, NULL, NULL }};
  533.   struct    ITEM_LIST MessageContextItem[] = { /* get the context for the folder */
  534.         { sizeof(MailContext), MAIL$_MESSAGE_FILE_CTX,
  535.             (char *)&MailContext, NULL },
  536.         { 0, 0, NULL, NULL }};
  537.   struct    ITEM_LIST ListFoldersItem[] = {    /* get a list of all folders */
  538.         { sizeof(char *), MAIL$_MAILFILE_FOLDER_ROUTINE,
  539.             (char *)vms_mail_list_folders, NULL },
  540.         { 0, 0, NULL, NULL } };
  541.   char    *p, *MailDirectory = NULL;    /* Mail directory if we find it */
  542.  
  543.   if (!stream) return &vmsmailproto;
  544.   ucase(stream->mailbox);    /* Will make it simpler for us later */
  545.   if(strcmp(stream->mailbox, "INBOX") == 0)
  546.     return NIL;
  547.  
  548.   if((p = strchr(stream->mailbox, '(')) != NULL) {
  549.     MailDirectory = malloc(strlen(p));
  550.     strcpy(MailDirectory, ++p);
  551.     if((p = strchr(MailDirectory, ')')) != NULL) {
  552.         *p = '\0';
  553.         if((p = strchr(stream->mailbox, ')')) != NULL)    /* Rewrite the folder name */
  554.             memmove(stream->mailbox, &p[1], strlen(p));
  555.                 /* Strlen will return one more - for the Null... */
  556.     }
  557.   }
  558.   else  MailDirectory = NULL;
  559.  
  560.   if((MailboxName = strrchr(stream->mailbox, '/')) != NULL)
  561.     memmove(stream->mailbox, &MailboxName[1], 
  562.         strlen(&MailboxName[1]) + 1);    /* Write the new name without the / */
  563.  
  564.                 /* return prototype for OP_PROTOTYPE call */
  565.   if (LOCAL && MailContext) {            /* close old file if stream being recycled */
  566.     vms_mail_close (stream);    /* dump and save the changes */
  567.     stream->dtb = &vmsmaildriver;    /* reattach this driver */
  568.     mail_free_cache (stream);    /* clean up cache */
  569.     MailContext = 0;        /* mark that it is free now */
  570.   }
  571.   else            /* No stream - just close */
  572.   if(MailContext) {
  573.     ___vms_mail_close();
  574.     MailContext = 0;
  575.   }
  576.  
  577.                 /* force readonly if bboard */
  578.   if (*stream->mailbox == '*') stream->rdonly = T;
  579.  
  580. /* Open the VMS file and select the folder we want */
  581.   ___vms_mail_open(MailDirectory);
  582.  
  583. /* Get the list of folders so we can check whether the requested folder
  584.    exists or not.
  585. */
  586.    FoldersNamesCount = 0;
  587.    mail$mailfile_info_file(&MailContext, ListFoldersItem, NullItemList);
  588.    for(i = 0; *FoldersNames[i] != '\0'; i++)
  589.     if(strcmp(FoldersNames[i], stream->mailbox) == 0)
  590.         break;
  591.    if(*FoldersNames[i] == '\0') {    /* Not found */
  592.     ___vms_mail_close();
  593.     mm_log("No such folder", NIL);
  594.     return NIL;
  595.    }
  596.  
  597.   MessageContext = 0;
  598.   status = mail$message_begin(&MessageContext, MessageContextItem, NullItemList);
  599. /* Create a context for accessing messages and folders */
  600.   if((status & 0x1) == 0) {
  601.       printf(" MessageBEgin failed, status=%d\n", status);
  602.       exit(status);
  603.   }
  604.  
  605.     stream->dtb = &vmsmaildriver;    /* reattach this driver */
  606.   stream->local = fs_get (sizeof (VMSMAILLOCAL));
  607.                 /* note if an INBOX or not */
  608.   LOCAL->inbox = (!strcmp (ucase (stream->mailbox),"INBOX") ||
  609.                   !strcmp (ucase (stream->mailbox),"NEWMAIL"));
  610.  
  611.   LOCAL->filesize = 0;        /* initialize parsed file size */
  612.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  613.   LOCAL->buflen = MAXMESSAGESIZE;
  614.   stream->sequence++;        /* bump sequence number */
  615.                 /* parse mailbox */
  616.   stream->nmsgs = stream->recent = 0;
  617.   if (vms_mail_ping (stream))
  618.     if(!stream->nmsgs)
  619.     mm_log ("Mailbox is empty",(long) NIL);
  620.   init_vms_mail_cache(stream->nmsgs);
  621.  
  622. /* Set the flags. If this is the newmail folder - All messages are defaulting
  623.    to NEW. If this is another folder set all messages to seen.
  624. */
  625.   if(strcmp(stream->mailbox, "NEWMAIL") != 0) {
  626.     sprintf(tmp, "1:%d", stream->nmsgs);
  627.     vms_mail_setflag (stream, tmp, "(\\SEEN)");
  628.   }
  629.   for (i = 1; i < stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
  630.   return stream;        /* return stream to caller */
  631. }
  632.  
  633. /* VMS/MAIL mail close
  634.  * Accepts: MAIL stream
  635.  */
  636.  
  637. void vms_mail_close (stream)
  638.     MAILSTREAM *stream;
  639. {
  640.   if (stream && MailContext) {    /* only if a file is open */
  641.     vms_file_newmail_messages(stream);    /* File the read messages from NEWMAIL to MAIL */
  642.     clear_vms_mail_cache(stream->nmsgs);
  643.     ___vms_mail_close();
  644.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  645.                 /* nuke the local data */
  646.     fs_give ((void **) &stream->local);
  647.     stream->dtb = NIL;        /* log out the DTB */
  648.   }
  649. }
  650.  
  651.  
  652.  
  653. /*
  654.  | Init the text cache.
  655.  */
  656. init_vms_mail_cache(nmsgs)
  657. int    nmsgs;
  658. {
  659.     int    i;
  660.  
  661.     if((MessageTextCache = malloc(nmsgs * sizeof(struct message_text_cache))) == NULL) {
  662.         perror("Malloc"); exit(1);
  663.     }
  664.  
  665. /* Clear all entries to null pointers */
  666.     for(i = 0; i < nmsgs; i++) {
  667.         MessageTextCache[i].text = NIL;
  668.         MessageTextCache[i].flags = 0;
  669.     }
  670. }
  671.  
  672.  
  673. /*
  674.  | Clear the text cache.
  675.  */
  676. clear_vms_mail_cache(nmsgs)
  677. int    nmsgs;
  678. {
  679.     int    i;
  680.  
  681.     if(nmsgs == 0) return;    /* Happens when new mail arrives in the middle of delete */
  682. /* Free all occiupied entries */
  683.     for(i = 0; i < nmsgs; i++)
  684.         if(MessageTextCache[i].text != NIL)
  685.             free(MessageTextCache[i].text);
  686.  
  687.     free(MessageTextCache);
  688. }
  689.  
  690.  
  691. /* VMS/MAIL mail fetch fast information
  692.  * Accepts: MAIL stream
  693.  *        sequence
  694.  */
  695.  
  696. void vms_mail_fetchfast (stream,sequence)
  697.     MAILSTREAM *stream;
  698.     char *sequence;
  699. {
  700.   return;            /* no-op for local mail */
  701. }
  702.  
  703.  
  704. /* VMS/MAIL mail fetch flags
  705.  * Accepts: MAIL stream
  706.  *        sequence
  707.  */
  708.  
  709. void vms_mail_fetchflags (stream,sequence)
  710.     MAILSTREAM *stream;
  711.     char *sequence;
  712. {
  713.   long i;
  714.   int    NewmailFolder;
  715.  
  716. /* If the message is from Newmail folder we mark it as new,
  717.    otherwise as read */
  718.   if(strcmp(stream->mailbox, "NEWMAIL") == 0)
  719.     NewmailFolder = 1;
  720.   else  NewmailFolder = 0;
  721.  
  722.                 /* ping mailbox, get new status for messages */
  723.   if (mail_sequence (stream,sequence))
  724.     for (i = 1; i <= stream->nmsgs; i++)
  725.       if (mail_elt (stream,i)->sequence) {
  726.     MESSAGECACHE *elt = mail_elt (stream,i);
  727.     elt->deleted = ((MessageTextCache[i - 1].flags & fDELETED) ? T : NIL);
  728.     elt->seen = ((MessageTextCache[i - 1].flags & fSEEN) ? T : NIL);
  729.     elt->flagged = ((MessageTextCache[i - 1].flags & fFLAGGED) ? T : NIL);
  730.     elt->answered = ((MessageTextCache[i - 1].flags & fANSWERED) ? T : NIL);
  731.      }
  732. }
  733.  
  734. /* VMS/MAIL mail fetch structure
  735.  * Accepts: MAIL stream
  736.  *        message # to fetch
  737.  *        pointer to return body
  738.  * Returns: envelope of this message, body returned in body value
  739.  *
  740.  * Fetches the "fast" information as well
  741.  */
  742. ENVELOPE *vms_mail_fetchstructure (stream,msgno,body)
  743.     MAILSTREAM *stream;
  744.     long msgno;
  745.     BODY **body;
  746. {
  747.   LONGCACHE *lelt;
  748.   ENVELOPE **env;
  749.   BODY **b;
  750.   STRING bs;
  751.   char *text, *hdr, *VMShdr;
  752.   unsigned long hdrsize = 0;
  753.   unsigned long hdrpos = 0;    /* Not implemented */
  754.   unsigned long textsize = vms_mail_size (stream,msgno);
  755.   unsigned long i = textsize;
  756.   char    record[256];    /* Mail record for reading */
  757.  
  758.   hdrsize = textsize;    /* Textsize includes both parts */
  759.   if (stream->scache) {        /* short cache */
  760.     if (msgno != stream->msgno){/* flush old poop if a different message */
  761.       mail_free_envelope (&stream->env);
  762.       mail_free_body (&stream->body);
  763.     }
  764.     stream->msgno = msgno;
  765.     env = &stream->env;        /* get pointers to envelope and body */
  766.     b = &stream->body;
  767.   }
  768.   else {            /* long cache */
  769.     lelt = mail_lelt (stream,msgno);
  770.     env = &lelt->env;        /* get pointers to envelope and body */
  771.     b = &lelt->body;
  772.   }
  773.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  774.     mail_free_envelope (env);    /* flush old envelope and body */
  775.     mail_free_body (b);
  776.     if (i > LOCAL->buflen) {    /* make sure enough buffer space */
  777.       fs_give ((void **) &LOCAL->buf);
  778.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  779.     }
  780.  
  781. /* Read the message into memory. First, allocate memory */
  782.     VMShdr = (char *) malloc (hdrsize + 1);
  783.     hdr = (char *) fs_get (hdrsize + 1);
  784.     text = (char *) fs_get (textsize + 1);
  785.     *VMShdr = *text = *hdr = '\0';
  786.  
  787. /* Now, VMS/MAIL brings us the message in a few parts: First the VMS/MAIL header,
  788.    then the text body which has optional RFC822 header and the text.
  789.    We first read the VMS/MAIL header and keep it. Then we check the first line
  790.    of the message's body. If it starts with some RFC822 keyword we know then
  791.    we take it as the RFC822 header. If not, we use the VMS/MAIL header as the
  792.    RFC822 one.
  793. */
  794.     while(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  795.     rewrite_vms_mail_from(record);    /* remove the :: */
  796.     strcat(VMShdr, record); strcat(VMShdr, "\n");
  797.     }
  798.  
  799. /* Now, check for RFC822 header */
  800.     if(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  801.     int    size = strlen(record);
  802.     if((search(record, size, "from:", 5) == T) ||
  803.        (search(record, size, "to:", 3) == T) ||
  804.        (search(record, size, "date:", 5) == T) ||
  805.        (search(record, size, "comment:", 8) == T) ||
  806.        (search(record, size, "return-path:", 12) == T) ||
  807.        (search(record, size, "received:", 9) == T)) {
  808.         /* Found it */
  809.         strcat(hdr, record); strcat(hdr, "\n");
  810.         if(search(record, size, "date:", 5) == T) {
  811.             char *p = &record[5]; while((*p == ' ') || (*p == '\t')) p++;
  812.             mail_parse_date (mail_elt (stream,msgno), p);
  813.         }
  814.         /* Copy the rest of the header */
  815.         while(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  816.             if(search(record, size, "date:", 5) == T) {
  817.                 char *p = &record[5]; while((*p == ' ') || (*p == '\t')) p++;
  818.                 mail_parse_date (mail_elt (stream,msgno), p);
  819.             }
  820.             strcat(hdr, record); strcat(hdr, "\n");
  821.         }
  822.         }
  823.         else {    /* No header detected - use VMS/MAIL header */
  824.         strcpy(hdr, VMShdr);
  825. /* Put some arbitrary date since there is no RFC822 header. */
  826.         mail_parse_date (mail_elt (stream,msgno), "Sun, 01 Jan 1990 00:00:00");
  827.         if(body) {
  828.             strcpy(text, record); strcat(text, "\n");    /* This is the first record of the text */
  829.         }
  830.         }
  831.     }
  832.     else    /* No header detected - and empty line read */
  833.     strcpy(hdr, VMShdr);
  834.  
  835.     strcat(hdr, "\n");    /* Add the "closing" line */
  836.  
  837. /* Now, the text */
  838.     if(body) {    /* If no body is needed then skip this stage */
  839.     int    size, CurrentSize = strlen(text);
  840.     while((size = get_vms_message_record(record, sizeof(record) - 1)) >= 0) {
  841.         strcpy(&text[CurrentSize], record);
  842.         text[CurrentSize += size] = '\n';    /* Append end of line */
  843.         CurrentSize++;    /* Next position after NL */
  844.     }
  845.     text[CurrentSize] = '\0';    /* Delimit text */
  846.     }
  847.  
  848.     free(VMShdr);
  849. /* Now we can have the real size */
  850.     hdrsize = strlen(hdr);
  851.     textsize = strlen(text);
  852.  
  853.     INIT (&bs,mail_string,(void *) text,textsize);
  854.                 /* parse envelope and body */
  855.     rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,mylocalhost(),LOCAL->buf);
  856.     (lelt->elt).rfc822_size = hdrsize + textsize;
  857.  
  858. /* Save the message's text in our buffer if it is not saved already */
  859.     if(body) {
  860.     if(MessageTextCache[msgno - 1].text == NIL) {    /* Not used yet */
  861.         MessageTextCache[msgno - 1].size = textsize;
  862.         MessageTextCache[msgno - 1].text = malloc(textsize + 1);
  863.         memcpy(MessageTextCache[msgno - 1].text, text, textsize + 1);
  864.     }
  865.     }
  866.  
  867.     fs_give ((void **) &text);
  868.     fs_give ((void **) &hdr);
  869.   }
  870.   if (body) *body = *b;        /* return the body */
  871.   return *env;            /* return the envelope */
  872. }
  873.  
  874. /*
  875.  * Get the next record from the message which should have been selected
  876.  * already. Return the record's size read. Trim-off trailing spaces.
  877.  */
  878. get_vms_message_record(record, size)
  879. char    *record;
  880. int    size;
  881. {
  882.     static int    RecordLength;    /* Returned by mail reading proc */
  883.     int    i, status;
  884.     struct    ITEM_LIST GetMessageTextItem[] = {
  885.         { sizeof(int), MAIL$_MESSAGE_CONTINUE, NULL, NULL },
  886.         { 0, 0, NULL, NULL }};
  887.     struct    ITEM_LIST RecordItem[] = {
  888.         { size, MAIL$_MESSAGE_RECORD, record, &RecordLength },
  889.         { 0, 0, NULL, NULL }};
  890.  
  891.     status = mail$message_get(&MessageContext, GetMessageTextItem,
  892.         RecordItem);
  893.     if((status & 0x1) == 0)
  894.         return -1;    /* Probably end of message */
  895.  
  896. /* Trim-off trailing spaces */
  897.     if(RecordLength > 0) {
  898.         while((RecordLength > 0) && (record[RecordLength - 1] == ' '))
  899.             RecordLength--;
  900.     }
  901.     record[RecordLength] = '\0';
  902.  
  903. /* Look for nulls inside the record (can't be inside C's strings...). Replace
  904.    them with spaces. */
  905.     for(i = 0; i < RecordLength; i++)
  906.         if(record[i] == '\0') record[i] = ' ';
  907.     return RecordLength;
  908. }
  909.  
  910.  
  911. /*
  912.  | VMS-MAIL uses :: to separate the username from the nodename; this drives
  913.  | PINE crazy since it thinks this is a list. We remove it. If the nodename
  914.  | is not the local one then we replace the xx::yy with yy@xx. Not very good
  915.  | but nothing better.
  916.  */
  917. rewrite_vms_mail_from(record)
  918. {
  919.     char    *p, from[256], *strstr();
  920.  
  921.     if(strncmp(record, "From:", 5) != 0) return;    /* Nothing to do */
  922.  
  923.     *from = '\0';
  924.     sscanf(record, "%*s %s", from);
  925.     if((p = strstr(from, "::")) != NULL) {
  926.         *p = '\0';
  927.         sprintf(record, "From:   %s@%s", &p[2], from);
  928.     }
  929. }
  930.  
  931. /* VMS/MAIL mail fetch message header
  932.  * Accepts: MAIL stream
  933.  *        message # to fetch
  934.  * Returns: message header in RFC822 format
  935.  */
  936.  
  937. char *vms_mail_fetchheader (stream,msgno)
  938.     MAILSTREAM *stream;
  939.     long msgno;
  940. {
  941.   int hdrsize = 0;
  942.   char    *hdr, *VMShdr;
  943.   char    record[256];
  944.  
  945.   hdrsize = vms_mail_size (stream,msgno);    /* Make this message current */
  946.   VMShdr = malloc(hdrsize);
  947.   hdr = malloc(hdrsize);
  948.   *VMShdr = *hdr = '\0';
  949.  
  950. /* The logic here is replicated from GetStruct */
  951.     while(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  952.     rewrite_vms_mail_from(record);    /* remove the :: */
  953.     strcat(VMShdr, record); strcat(VMShdr, "\n");
  954.     }
  955.  
  956. /* Now, check for RFC822 header */
  957.     if(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  958.     int    size = strlen(record);
  959.     if((search(record, size, "from:", 5) == T) ||
  960.        (search(record, size, "to:", 3) == T) ||
  961.        (search(record, size, "date:", 5) == T) ||
  962.        (search(record, size, "return-path:", 12) == T) ||
  963.        (search(record, size, "comment:", 8) == T) ||
  964.        (search(record, size, "received:", 9) == T)) {
  965.         /* Found it */
  966.         strcpy(hdr, record); strcat(hdr, "\n");
  967.         /* Copy the rest of the header */
  968.         while(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  969.             strcat(hdr, record); strcat(hdr, "\n");
  970.         }
  971.         }
  972.         else {    /* No header detected - use VMS/MAIL header */
  973.         strcpy(hdr, VMShdr);
  974.         }
  975.     }
  976.     else    /* No header detected - and empty line read */
  977.     strcpy(hdr, VMShdr);
  978.  
  979.  
  980.     strcat(hdr, "\n");    /* Add the empty line which ends the header */
  981.     free(VMShdr);
  982. /* Now we can have the real size */
  983.     hdrsize = strlen(hdr);
  984.  
  985.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,hdr,hdrsize);
  986.   free(hdr);
  987.   return LOCAL->buf;
  988. }
  989.  
  990.  
  991. /* VMS/MAIL mail fetch message text (only)
  992.     body only;
  993.  * Accepts: MAIL stream
  994.  *        message # to fetch
  995.  * Returns: message text in RFC822 format
  996.  */
  997.  
  998. char *vms_mail_fetchtext (stream,msgno)
  999.     MAILSTREAM *stream;
  1000.     long msgno;
  1001. {
  1002.   unsigned long hdrsize;
  1003.   unsigned long textsize = vms_mail_size (stream,msgno);
  1004.   char    *VMShdr, *hdr, *text;
  1005.   char    record[256];
  1006.  
  1007. /* Mark message as seen */
  1008.   if(strcmp(stream->mailbox, "NEWMAIL") == 0) {
  1009.     char    sequence[256];
  1010.     sprintf(sequence, "%d", msgno);    /* Write message number as string */
  1011.     vms_mail_setflag (stream,sequence,"(\\SEEN)");
  1012.   }
  1013.  
  1014.   hdrsize = textsize;
  1015.   mail_elt (stream,msgno)->seen = T;
  1016.                 /* recalculate status */
  1017.   vms_mail_update_status (stream,msgno,T);
  1018.                 /* get to text position */
  1019.  
  1020.  
  1021.   hdr = malloc(hdrsize);
  1022.   text = malloc(hdrsize);
  1023.   VMShdr = malloc(hdrsize);
  1024.   *VMShdr = *text = *hdr = '\0';
  1025.  
  1026. /* The logic here is replicated from GetStruct */
  1027.     while(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  1028.     rewrite_vms_mail_from(record);    /* remove the :: */
  1029.     strcat(VMShdr, record); strcat(VMShdr, "\n");
  1030.     }
  1031.  
  1032. /* Now, check for RFC822 header */
  1033.     if(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  1034.     int    size = strlen(record);
  1035.     if((search(record, size, "from:", 5) == T) ||
  1036.        (search(record, size, "to:", 3) == T) ||
  1037.        (search(record, size, "date:", 5) == T) ||
  1038.        (search(record, size, "return-path:", 12) == T) ||
  1039.        (search(record, size, "comment:", 8) == T) ||
  1040.        (search(record, size, "received:", 9) == T)) {
  1041.         /* Found it */
  1042.         strcat(hdr, record); strcat(hdr, "\n");
  1043.         /* Copy the rest of the header */
  1044.         while(get_vms_message_record(record, sizeof(record) - 1) > 0) {
  1045.             strcat(hdr, record); strcat(hdr, "\n");
  1046.         }
  1047.         }
  1048.         else {    /* No header detected - use VMS/MAIL header */
  1049.         strcpy(hdr, VMShdr);
  1050.         strcpy(text, record); strcat(text, "\n");    /* This is the first record of the text */
  1051.         }
  1052.     }
  1053.     else    /* No header detected - and empty line read */
  1054.     strcpy(hdr, VMShdr);
  1055.     strcat(hdr, "\n");    /* Add the empty line which ends the header */
  1056.  
  1057. /* Now, the text */
  1058.     {
  1059.     int    size, CurrentSize = strlen(text);
  1060.     while((size = get_vms_message_record(record, sizeof(record) - 1)) >= 0) {
  1061.         strcpy(&text[CurrentSize], record);
  1062.         text[CurrentSize += size] = '\n';    /* Append end of line */
  1063.         CurrentSize++;    /* Next position after NL */
  1064.     }
  1065.     text[CurrentSize] = '\0';    /* Delimit text */
  1066.     }
  1067.  
  1068. /* Now we can have the real size */
  1069.     hdrsize = strlen(hdr);
  1070.     textsize = strlen(text);
  1071.     free(VMShdr);
  1072.  
  1073.                 /* copy the string */
  1074.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,text,textsize);
  1075.   free(text);
  1076.   free(hdr);
  1077.   return LOCAL->buf;
  1078. }
  1079.  
  1080.  
  1081.  
  1082. /* VMS/MAIL fetch message body as a structure
  1083.  * Accepts: Mail stream
  1084.  *        message # to fetch
  1085.  *        section specifier
  1086.  *        pointer to length
  1087.  * Returns: pointer to section of message body
  1088.  */
  1089. char *vms_mail_fetchbody (stream,m,s,len)
  1090.     MAILSTREAM *stream;
  1091.     long m;
  1092.     char *s;
  1093.     unsigned long *len;
  1094. {
  1095.   BODY *b;
  1096.   PART *pt;
  1097.   char *t;
  1098.   ENVELOPE *env;
  1099.   unsigned long i;
  1100.   unsigned long offset = 0;
  1101.   MESSAGECACHE *elt = mail_elt (stream,m);
  1102.  
  1103. /* If this is the NEWMAIL then set the "SEEN" flag so we know to file it at
  1104.    MAIL folder when we are done.
  1105. */
  1106.   if(strcmp(stream->mailbox, "NEWMAIL") == 0) {
  1107.     char    sequence[256];
  1108.     sprintf(sequence, "%d", m);    /* Write message number as string */
  1109.     vms_mail_setflag (stream,sequence,"(\\SEEN)");
  1110.   }
  1111.  
  1112.                 /* make sure have a body */
  1113.   env = vms_mail_fetchstructure (stream,m,&b);
  1114.   if (!(env && b && s && *s &&
  1115.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  1116.   do {                /* until find desired body part */
  1117.                 /* multipart content? */
  1118.     if (b->type == TYPEMULTIPART) {
  1119.       pt = b->contents.part;    /* yes, find desired part */
  1120.       while (--i && (pt = pt->next));
  1121.       if (!pt) return NIL;    /* bad specifier */
  1122.                 /* note new body, check valid nesting */
  1123.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  1124.       offset = pt->offset;    /* get new offset */
  1125.     }
  1126.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  1127.                 /* need to go down further? */
  1128.     if (i = *s) switch (b->type) {
  1129.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  1130.       offset = b->contents.msg.offset;
  1131.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  1132.     case TYPEMULTIPART:        /* multipart, get next section */
  1133.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  1134.     default:            /* bogus subpart specification */
  1135.       return NIL;
  1136.     }
  1137.   } while (i);
  1138.                 /* lose if body bogus */
  1139.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  1140.                 /* move to that place in the data */
  1141.  
  1142. /*  lseek (LOCAL->fd,offset,L_SET); */
  1143.   t = (char *) fs_get (1 + b->size.ibytes);
  1144.  
  1145.   strncpy(t, &((MessageTextCache[m - 1].text)[offset]), b->size.ibytes);
  1146.  
  1147. /*  read (LOCAL->fd,t,b->size.ibytes); */
  1148.  
  1149.   rfc822_contents(&LOCAL->buf,&LOCAL->buflen,len,t,b->size.ibytes,b->encoding);
  1150.   fs_give ((void **) &t);    /* flush readin buffer */
  1151.   return LOCAL->buf;
  1152. }
  1153.  
  1154. /* VMS/MAIL mail set flag
  1155.  * Accepts: MAIL stream
  1156.  *        sequence
  1157.  *        flag(s)
  1158.  */
  1159.  
  1160. void vms_mail_setflag (stream,sequence,flag)
  1161.     MAILSTREAM *stream;
  1162.     char *sequence;
  1163.     char *flag;
  1164. {
  1165.   int    i;
  1166.   MESSAGECACHE *elt;
  1167.  
  1168.   short f = vms_mail_getflags (stream,flag);    /* Parse the list */
  1169.   if (!f) return;        /* no-op if no flags to modify */
  1170.                 /* get sequence and loop on it */
  1171.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  1172.     if ((elt = mail_elt (stream,i))->sequence) {
  1173.                 /* set all requested flags */
  1174.       if (f&fSEEN) {
  1175.         elt->seen = T;
  1176.         MessageTextCache[i - 1].flags |= fSEEN;
  1177.       }
  1178.       if (f&fDELETED) {
  1179.         elt->deleted = T;
  1180.         MessageTextCache[i - 1].flags |= fDELETED;
  1181.       }
  1182.       if (f&fFLAGGED) elt->flagged = T;
  1183.       if (f&fANSWERED) elt->answered = T;
  1184.     }
  1185. }
  1186.  
  1187. /* VMS/MAIL mail clear flag
  1188.  * Accepts: MAIL stream
  1189.  *        sequence
  1190.  *        flag(s)
  1191.  */
  1192.  
  1193. void vms_mail_clearflag (stream,sequence,flag)
  1194.     MAILSTREAM *stream;
  1195.     char *sequence;
  1196.     char *flag;
  1197. {
  1198.   int    i;
  1199.   MESSAGECACHE *elt;
  1200.  
  1201.   short f = vms_mail_getflags (stream,flag);    /* Parse the list */
  1202.   if (!f) return;        /* no-op if no flags to modify */
  1203.                 /* get sequence and loop on it */
  1204.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  1205.     if ((elt = mail_elt (stream,i))->sequence) {
  1206.                 /* set all requested flags */
  1207.       if (f&fSEEN) {
  1208.         elt->seen = NIL;
  1209.         MessageTextCache[i - 1].flags &= ~fSEEN;
  1210.       }
  1211.       if (f&fDELETED) {
  1212.         elt->deleted = NIL;
  1213.         MessageTextCache[i - 1].flags &= ~fDELETED;
  1214.       }
  1215.       if (f&fFLAGGED) elt->flagged = NIL;
  1216.       if (f&fANSWERED) elt->answered = NIL;
  1217.     }
  1218. }
  1219.  
  1220. /* VMS/MAIL mail search for messages
  1221.  * Accepts: MAIL stream
  1222.  *        search criteria
  1223.  */
  1224.  
  1225. void vms_mail_search (stream,criteria)
  1226.     MAILSTREAM *stream;
  1227.     char *criteria;
  1228. {
  1229.   long i,n;
  1230.   char *d;
  1231.   search_t f;
  1232.                 /* initially all searched */
  1233.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  1234.                 /* get first criterion */
  1235.   if (criteria && (criteria = strtok (criteria," "))) {
  1236.                 /* for each criterion */
  1237.     for (; criteria; (criteria = strtok (NIL," "))) {
  1238.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  1239.       switch (*ucase (criteria)) {
  1240.       case 'A':            /* possible ALL, ANSWERED */
  1241.     if (!strcmp (criteria+1,"LL")) f = vms_mail_search_all;
  1242.     else if (!strcmp (criteria+1,"NSWERED")) f = vms_mail_search_answered;
  1243.     break;
  1244.       case 'B':            /* possible BCC, BEFORE, BODY */
  1245.     if (!strcmp (criteria+1,"CC"))
  1246.       f = vms_mail_search_string (vms_mail_search_bcc,&d,&n);
  1247.     else if (!strcmp (criteria+1,"EFORE"))
  1248.       f = vms_mail_search_date (vms_mail_search_before,&n);
  1249.     else if (!strcmp (criteria+1,"ODY"))
  1250.       f = vms_mail_search_string (vms_mail_search_body,&d,&n);
  1251.     break;
  1252.       case 'C':            /* possible CC */
  1253.     if (!strcmp (criteria+1,"C"))
  1254.       f = vms_mail_search_string (vms_mail_search_cc,&d,&n);
  1255.     break;
  1256.       case 'D':            /* possible DELETED */
  1257.     if (!strcmp (criteria+1,"ELETED")) f = vms_mail_search_deleted;
  1258.     break;
  1259.       case 'F':            /* possible FLAGGED, FROM */
  1260.     if (!strcmp (criteria+1,"LAGGED")) f = vms_mail_search_flagged;
  1261.     else if (!strcmp (criteria+1,"ROM"))
  1262.       f = vms_mail_search_string (vms_mail_search_from,&d,&n);
  1263.     break;
  1264.       case 'K':            /* possible KEYWORD */
  1265.     if (!strcmp (criteria+1,"EYWORD"))
  1266.       f = vms_mail_search_flag (vms_mail_search_keyword,&n,stream);
  1267.     break;
  1268.       case 'N':            /* possible NEW */
  1269.     if (!strcmp (criteria+1,"EW")) f = vms_mail_search_new;
  1270.     break;
  1271.  
  1272.       case 'O':            /* possible OLD, ON */
  1273.     if (!strcmp (criteria+1,"LD")) f = vms_mail_search_old;
  1274.     else if (!strcmp (criteria+1,"N"))
  1275.       f = vms_mail_search_date (vms_mail_search_on,&n);
  1276.     break;
  1277.       case 'R':            /* possible RECENT */
  1278.     if (!strcmp (criteria+1,"ECENT")) f = vms_mail_search_recent;
  1279.     break;
  1280.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  1281.     if (!strcmp (criteria+1,"EEN")) f = vms_mail_search_seen;
  1282.     else if (!strcmp (criteria+1,"INCE"))
  1283.       f = vms_mail_search_date (vms_mail_search_since,&n);
  1284.     else if (!strcmp (criteria+1,"UBJECT"))
  1285.       f = vms_mail_search_string (vms_mail_search_subject,&d,&n);
  1286.     break;
  1287.       case 'T':            /* possible TEXT, TO */
  1288.     if (!strcmp (criteria+1,"EXT"))
  1289.       f = vms_mail_search_string (vms_mail_search_text,&d,&n);
  1290.     else if (!strcmp (criteria+1,"O"))
  1291.       f = vms_mail_search_string (vms_mail_search_to,&d,&n);
  1292.     break;
  1293.       case 'U':            /* possible UN* */
  1294.     if (criteria[1] == 'N') {
  1295.       if (!strcmp (criteria+2,"ANSWERED")) f = vms_mail_search_unanswered;
  1296.       else if (!strcmp (criteria+2,"DELETED")) f = vms_mail_search_undeleted;
  1297.       else if (!strcmp (criteria+2,"FLAGGED")) f = vms_mail_search_unflagged;
  1298.       else if (!strcmp (criteria+2,"KEYWORD"))
  1299.         f = vms_mail_search_flag (vms_mail_search_unkeyword,&n,stream);
  1300.       else if (!strcmp (criteria+2,"SEEN")) f = vms_mail_search_unseen;
  1301.     }
  1302.     break;
  1303.       default:            /* we will barf below */
  1304.     break;
  1305.       }
  1306.       if (!f) {            /* if can't determine any criteria */
  1307.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  1308.     mm_log (LOCAL->buf,ERROR);
  1309.     return;
  1310.       }
  1311.                 /* run the search criterion */
  1312.       for (i = 1; i <= stream->nmsgs; ++i)
  1313.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  1314.       mail_elt (stream,i)->searched = NIL;
  1315.     }
  1316.                 /* report search results to main program */
  1317.     for (i = 1; i <= stream->nmsgs; ++i)
  1318.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  1319.   }
  1320. }
  1321.  
  1322. /* VMS/MAIL mail ping mailbox - find how many messages are in this folder.
  1323.  * Do it only when openning the folder. TotalMessagesCount remembers the number
  1324.  * and we do not check again in the middle to not confuse the caching system.
  1325.  * Accepts: MAIL stream
  1326.  * Returns: T if stream still alive, NIL if not
  1327.  */
  1328. long vms_mail_ping (stream)
  1329.     MAILSTREAM *stream;
  1330. {
  1331.   long r = NIL;
  1332.   int    status;
  1333.   static unsigned int MessagesCount;
  1334.   struct    ITEM_LIST FolderItem[] = {    /* Folder's name */
  1335.         { 0, MAIL$_MESSAGE_FOLDER, NULL, NULL }, /* Must be the first item!!! */
  1336.         { 0, 0, NULL, NULL }};
  1337.   struct    ITEM_LIST MessagesCountItem[] = { /* How many mesages are there */
  1338.         { sizeof(MessagesCount), MAIL$_MESSAGE_SELECTED,
  1339.           (char *)&MessagesCount, NULL },
  1340.         { 0, 0, NULL, NULL }};
  1341.   struct    ITEM_LIST NullItemList[] = {
  1342.         { 0, 0, NULL, NULL }};
  1343.  
  1344.   if(strcmp(stream->mailbox, "INBOX") == NULL)
  1345.     return NIL;
  1346.  
  1347.   if(TotalMessagesCount == -1) {    /* Not checked yet */
  1348.     FolderItem[0].buffer = stream->mailbox;
  1349.     FolderItem[0].length = strlen(stream->mailbox);
  1350.  
  1351.     status = mail$message_select(&MessageContext, FolderItem, MessagesCountItem);
  1352.     if((status & 0x1) == 0) {
  1353.         printf("No such folder, status=%d\n", status);
  1354.         return NIL;
  1355.     }
  1356.     TotalMessagesCount = MessagesCount;
  1357.    }
  1358.    else    MessagesCount = TotalMessagesCount;    /* Ignore mails added in the middle */
  1359.  
  1360.   stream->nmsgs = MessagesCount;
  1361.  
  1362.   if(MessagesCount != 0)
  1363.     return T;
  1364.   else    return NIL;
  1365. }
  1366.  
  1367.  
  1368. /* VMS/MAIL mail check mailbox (too)
  1369.     reparses status too;
  1370.  * Accepts: MAIL stream
  1371.  */
  1372.  
  1373. void vms_mail_check (stream)
  1374.     MAILSTREAM *stream;
  1375. {
  1376.   mm_log ("Check completed",(long) NIL);
  1377. }
  1378.  
  1379. /* VMS/MAIL mail expunge mailbox - Delete the messages that are flagged as
  1380.  * deleted.
  1381.  * Accepts: MAIL stream
  1382.  */
  1383.  
  1384. void vms_mail_expunge (stream)
  1385. MAILSTREAM *stream;
  1386. {
  1387.     static int    MessageNum;    /* Message number to delete */
  1388.     struct    ITEM_LIST DeleteMessageItem[] = {
  1389.         { sizeof(MessageNum), MAIL$_MESSAGE_ID, &MessageNum, NULL },
  1390.         { 0, 0, NULL, NULL }};
  1391.     struct    ITEM_LIST    NullItem[] = { 0, 0, NULL, NULL };
  1392.     int    i, status, MessagesDeleted = 0;    /* How many messages were deleted */
  1393.  
  1394.   for (i = 1; i <= stream->nmsgs; ++i) {
  1395.     if(MessageTextCache[i - 1].flags & fDELETED) {
  1396.             MessagesDeleted++;
  1397. /* In NEWMAIL we are called twice, so mark which message was deleted and do not
  1398.    try deleting it again. */
  1399.         if((MessageTextCache[i - 1].flags & fWAS_DELETED) != fWAS_DELETED) {
  1400.             MessageNum = i;    /* Assign it at the descriptor */
  1401.             status = mail$message_delete(&MessageContext,
  1402.                 &DeleteMessageItem, NullItem);
  1403.             if((status & 0x1) == 0)
  1404.                 exit(status);
  1405.             MessageTextCache[i - 1].flags |= fWAS_DELETED;
  1406.         }
  1407.     }
  1408.   }
  1409.  
  1410.   if((MessagesDeleted) &&    /* Some messages were removed */
  1411.      (strcmp(stream->mailbox, "NEWMAIL") == 0))    /* And it is NEWMAIL */
  1412.     vms_set_new_mail_count(stream->nmsgs - MessagesDeleted);    /* Set the new count */
  1413. }
  1414.  
  1415.  
  1416. /*
  1417.  | set the new number of messages. THere is no automatic way...
  1418.  */
  1419. vms_set_new_mail_count(NewCount)
  1420. {
  1421.   static int    UserContext;
  1422.   static short    MessagesCount;
  1423.   int        status, mail$user_begin(), mail$user_end(), mail$user_set_info();
  1424.   struct    ITEM_LIST MessagesCountItem[] = {
  1425.         { sizeof(MessagesCount), MAIL$_USER_SET_NEW_MESSAGES,
  1426.           (char *)&MessagesCount, NULL },
  1427.         { 0, 0, NULL, NULL }};
  1428.   struct    ITEM_LIST NullItemList[] = {
  1429.         { 0, 0, NULL, NULL }};
  1430.  
  1431.   UserContext = 0;
  1432.   status = mail$user_begin(&UserContext, NullItemList, NullItemList);
  1433.   if((status & 0x1) == 0) {
  1434.     printf("Mail$user_begin failed with %d\n", status);
  1435.     return;
  1436.   }
  1437.  
  1438.   MessagesCount = (short) NewCount;
  1439.  
  1440.   status = mail$user_set_info(&UserContext, MessagesCountItem, NullItemList);
  1441.   if((status & 0x1) == 0) {
  1442.     printf("Mail$set_info failed with %d\n", status);
  1443.     return;
  1444.   }
  1445.  
  1446.   status = mail$user_end(&UserContext, NullItemList, NullItemList);
  1447.   if((status & 0x1) == 0) {
  1448.     printf("Mail$user_end failed with %d\n", status);
  1449.     return;
  1450.   }
  1451. }
  1452.  
  1453.  
  1454.  
  1455. /*
  1456.  | Get the mail directory from mail's routines.
  1457.  */
  1458. vms_mail_get_user_directory(username, home)
  1459. char    *username, **home;
  1460. {
  1461.   static int    UserContext, length;
  1462.   static char    MailDirectory[256];    /* Must be static as we return it */
  1463.   int        status, mail$user_begin(), mail$user_end(), mail$user_get_info();
  1464.   struct    ITEM_LIST InItemList[] = {
  1465.         { strlen(username), MAIL$_USER_USERNAME, (char *)username, NULL},
  1466.         { 0, 0, NULL, NULL }};
  1467.   struct    ITEM_LIST OutItemList[] = {
  1468.         { sizeof(MailDirectory) - 1, MAIL$_USER_FULL_DIRECTORY,
  1469.             (char *)MailDirectory, &length},
  1470.         { 0, 0, NULL, NULL }};
  1471.   struct    ITEM_LIST NullItemList[] = {
  1472.         { 0, 0, NULL, NULL }};
  1473.  
  1474.   UserContext = 0;
  1475.   status = mail$user_begin(&UserContext, NullItemList, NullItemList);
  1476.   if((status & 0x1) == 0) {
  1477.     printf("Mail$user_begin failed with %d\n", status);
  1478.     return;
  1479.   }
  1480.  
  1481.   status = mail$user_get_info(&UserContext, InItemList, OutItemList);
  1482.   if((status & 0x1) == 0) {
  1483.     printf("Mail$get_info failed with %d\n", status);
  1484.     return;
  1485.   }
  1486.  
  1487.   status = mail$user_end(&UserContext, NullItemList, NullItemList);
  1488.   if((status & 0x1) == 0) {
  1489.     printf("Mail$user_end failed with %d\n", status);
  1490.     return;
  1491.   }
  1492.  
  1493.   MailDirectory[length & 0xffff] = '\0';
  1494.   *home = MailDirectory;
  1495. }
  1496.  
  1497. /* VMS/MAIL mail copy message(s)
  1498.     s;
  1499.  * Accepts: MAIL stream
  1500.  *        sequence
  1501.  *        destination mailbox
  1502.  * Returns: T if success, NIL if failed
  1503.  */
  1504.  
  1505. long vms_mail_copy (stream,sequence,mailbox)
  1506.     MAILSTREAM *stream;
  1507.     char *sequence;
  1508.     char *mailbox;
  1509. {
  1510.                 /* copy the messages */
  1511.   return (mail_sequence (stream,sequence)) ?
  1512.     vms_mail_copy_messages (stream,mailbox) : NIL;
  1513. }
  1514.  
  1515.  
  1516. /* VMS/MAIL mail move message(s)
  1517.     s;
  1518.  * Accepts: MAIL stream
  1519.  *        sequence
  1520.  *        destination mailbox
  1521.  * Returns: T if success, NIL if failed
  1522.  */
  1523.  
  1524. long vms_mail_move (stream,sequence,mailbox)
  1525.     MAILSTREAM *stream;
  1526.     char *sequence;
  1527.     char *mailbox;
  1528. {
  1529.   long i;
  1530.   MESSAGECACHE *elt;
  1531.   if (!(mail_sequence (stream,sequence) &&
  1532.     vms_mail_copy_messages (stream,mailbox))) return NIL;
  1533.                 /* delete all requested messages */
  1534.   for (i = 1; i <= stream->nmsgs; i++)
  1535.     if ((elt = mail_elt (stream,i))->sequence) {
  1536.       elt->deleted = T;        /* mark message deleted */
  1537.                 /* recalculate status */
  1538.       vms_mail_update_status (stream,i,NIL);
  1539.     }
  1540.                 /* make sure the update takes */
  1541.   return LONGT;
  1542. }
  1543.  
  1544. /* VMS/MAIL mail append message from stringstruct
  1545.  * Accepts: MAIL stream
  1546.  *        destination mailbox
  1547.  *        stringstruct of messages to append
  1548.  * Returns: T if append successful, else NIL
  1549.  */
  1550.  
  1551. long vms_mail_append (stream,mailbox,message)
  1552.     MAILSTREAM *stream;
  1553.     char *mailbox;
  1554.     STRING *message;
  1555. {
  1556.     return NIL;    /* Do nothing */
  1557. }
  1558.  
  1559. /* VMS/MAIL garbage collect stream
  1560.  * Accepts: Mail stream
  1561.  *        garbage collection flags
  1562.  */
  1563.  
  1564. void vms_mail_gc (stream,gcflags)
  1565.     MAILSTREAM *stream;
  1566.     long gcflags;
  1567. {
  1568.   /* nothing here for now */
  1569. }
  1570.  
  1571. /* Internal routines */
  1572.  
  1573.  
  1574. /* VMS/MAIL mail lock file for parse/append permission
  1575.  * Accepts: file descriptor
  1576.  *        lock file name buffer
  1577.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1578.  * Returns: file descriptor of lock or -1 if failure
  1579.  */
  1580.  
  1581. int vms_mail_lock (fd,lock,op)
  1582. {
  1583.     return;
  1584. }
  1585.  
  1586.  
  1587. /* VMS/MAIL mail unlock file for parse/append permission
  1588.  * Accepts: file descriptor
  1589.  *        lock file name from vms_mail_lock()
  1590.  */
  1591.  
  1592. void vms_mail_unlock (fd,lock)
  1593. {
  1594.   return;
  1595. }
  1596.  
  1597. /* VMS/MAIL mail return internal message size in bytes. Since we get the size
  1598.  * in records we multiply it by 256 which is the maximal record length that
  1599.  * VMS/MAIL is willing to give us.
  1600.  * Accepts: MAIL stream
  1601.  *        message #
  1602.  * Returns: internal size of message
  1603.  */
  1604. unsigned long vms_mail_size (stream,m)
  1605.     MAILSTREAM *stream;
  1606.     long m;
  1607. {
  1608.   MESSAGECACHE *elt;
  1609.   static int    MessageSize;
  1610.   int    status;
  1611.   struct    ITEM_LIST GetMessageInfoItem[] = {
  1612.         { sizeof(m), MAIL$_MESSAGE_ID,
  1613.             (char *)&m, NULL },
  1614.         { 0, 0, NULL, NULL }};
  1615.     struct    ITEM_LIST AnswerMessageInfoItem[] = {
  1616.         { sizeof(int), MAIL$_MESSAGE_SIZE, (char *)&MessageSize, NULL },
  1617.         { 0, 0, NULL, NULL }};
  1618.  
  1619.   elt = mail_elt (stream,m);
  1620.  
  1621.     status = mail$message_get(&MessageContext, GetMessageInfoItem,
  1622.             AnswerMessageInfoItem);
  1623.     if((status & 0x1) == 0) {
  1624.         printf(" GetMessage header failed, status=%d\n", status);
  1625.         exit(status);
  1626.     }
  1627.  
  1628.     if(MessageSize == 0) MessageSize = 1;    /* So all Malloc's won't fail */
  1629.     elt->data1 = elt->data2 = MessageSize * 256;
  1630.     return (elt->data1);
  1631. }
  1632.  
  1633. /* Parse flag list
  1634.  * Accepts: MAIL stream
  1635.  *        flag list as a character string
  1636.  * Returns: flag command list
  1637.  */
  1638.  
  1639.  
  1640. short vms_mail_getflags (stream,flag)
  1641.     MAILSTREAM *stream;
  1642.     char *flag;
  1643. {
  1644.   char *t;
  1645.   short f = 0;
  1646.   short i,j;
  1647.   if (flag && *flag) {        /* no-op if no flag string */
  1648.                 /* check if a list and make sure valid */
  1649.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1650.       mm_log ("Bad flag list",ERROR);
  1651.       return NIL;
  1652.     }
  1653.                 /* copy the flag string w/o list construct */
  1654.     strncpy (LOCAL->buf,flag+i,(j = strlen (flag) - (2*i)));
  1655.     LOCAL->buf[j] = '\0';
  1656.     t = ucase (LOCAL->buf);    /* uppercase only from now on */
  1657.  
  1658.     while (*t) {        /* parse the flags */
  1659.       if (*t == '\\') {        /* system flag? */
  1660.     switch (*++t) {        /* dispatch based on first character */
  1661.     case 'S':        /* possible \Seen flag */
  1662.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  1663.       t += 4;        /* skip past flag name */
  1664.       break;
  1665.     case 'D':        /* possible \Deleted flag */
  1666.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1667.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  1668.       t += 7;        /* skip past flag name */
  1669.       break;
  1670.     case 'F':        /* possible \Flagged flag */
  1671.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1672.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  1673.       t += 7;        /* skip past flag name */
  1674.       break;
  1675.     case 'A':        /* possible \Answered flag */
  1676.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1677.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  1678.       t += 8;        /* skip past flag name */
  1679.       break;
  1680.     default:        /* unknown */
  1681.       i = 0;
  1682.       break;
  1683.     }
  1684.                 /* add flag to flags list */
  1685.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1686.     else {            /* bitch about bogus flag */
  1687.       mm_log ("Unknown system flag",ERROR);
  1688.       return NIL;
  1689.     }
  1690.       }
  1691.       else {            /* no user flags yet */
  1692.     mm_log ("Unknown flag",ERROR);
  1693.     return NIL;
  1694.       }
  1695.     }
  1696.   }
  1697.   return f;
  1698. }
  1699.  
  1700. /* VMS/MAIL copy messages
  1701.  * Accepts: MAIL stream
  1702.  *        mailbox copy vector
  1703.  *        mailbox name
  1704.  * Returns: T if success, NIL if failed
  1705.  */
  1706.  
  1707. long vms_mail_copy_messages (stream,mailbox)
  1708.     MAILSTREAM *stream;
  1709.     char *mailbox;
  1710. {
  1711.     return NIL;
  1712. }
  1713.  
  1714. /* VMS/MAIL update status string
  1715.  * Accepts: MAIL stream
  1716.  *        message number
  1717.  *        flag saying whether or not to sync
  1718.  */
  1719.  
  1720. void vms_mail_update_status (stream,msgno,syncflag)
  1721.     MAILSTREAM *stream;
  1722.     long msgno;
  1723.     long syncflag;
  1724. {
  1725. /*~~~~ Does nothing at preset. */
  1726. }
  1727.  
  1728. /* Search support routines
  1729.  * Accepts: MAIL stream
  1730.  *        message number
  1731.  *        pointer to additional data
  1732.  * Returns: T if search matches, else NIL
  1733.  */
  1734.  
  1735.  
  1736. char vms_mail_search_all (stream,msgno,d,n)
  1737.     MAILSTREAM *stream;
  1738.     long msgno;
  1739.     char *d;
  1740.     long n;
  1741. {
  1742.   return T;            /* ALL always succeeds */
  1743. }
  1744.  
  1745.  
  1746. char vms_mail_search_answered (stream,msgno,d,n)
  1747.     MAILSTREAM *stream;
  1748.     long msgno;
  1749.     char *d;
  1750.     long n;
  1751. {
  1752.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1753. }
  1754.  
  1755.  
  1756. char vms_mail_search_deleted (stream,msgno,d,n)
  1757.     MAILSTREAM *stream;
  1758.     long msgno;
  1759.     char *d;
  1760.     long n;
  1761. {
  1762.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1763. }
  1764.  
  1765.  
  1766. char vms_mail_search_flagged (stream,msgno,d,n)
  1767.     MAILSTREAM *stream;
  1768.     long msgno;
  1769.     char *d;
  1770.     long n;
  1771. {
  1772.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1773. }
  1774.  
  1775.  
  1776. char vms_mail_search_keyword (stream,msgno,d,n)
  1777.     MAILSTREAM *stream;
  1778.     long msgno;
  1779.     char *d;
  1780.     long n;
  1781. {
  1782.   return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
  1783. }
  1784.  
  1785.  
  1786. char vms_mail_search_new (stream,msgno,d,n)
  1787.     MAILSTREAM *stream;
  1788.     long msgno;
  1789.     char *d;
  1790.     long n;
  1791. {
  1792.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1793.   return (elt->recent && !elt->seen) ? T : NIL;
  1794. }
  1795.  
  1796. char vms_mail_search_old (stream,msgno,d,n)
  1797.     MAILSTREAM *stream;
  1798.     long msgno;
  1799.     char *d;
  1800.     long n;
  1801. {
  1802.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1803. }
  1804.  
  1805.  
  1806. char vms_mail_search_recent (stream,msgno,d,n)
  1807.     MAILSTREAM *stream;
  1808.     long msgno;
  1809.     char *d;
  1810.     long n;
  1811. {
  1812.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1813. }
  1814.  
  1815.  
  1816. char vms_mail_search_seen (stream,msgno,d,n)
  1817.     MAILSTREAM *stream;
  1818.     long msgno;
  1819.     char *d;
  1820.     long n;
  1821. {
  1822.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1823. }
  1824.  
  1825.  
  1826. char vms_mail_search_unanswered (stream,msgno,d,n)
  1827.     MAILSTREAM *stream;
  1828.     long msgno;
  1829.     char *d;
  1830.     long n;
  1831. {
  1832.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1833. }
  1834.  
  1835.  
  1836. char vms_mail_search_undeleted (stream,msgno,d,n)
  1837.     MAILSTREAM *stream;
  1838.     long msgno;
  1839.     char *d;
  1840.     long n;
  1841. {
  1842.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1843. }
  1844.  
  1845.  
  1846. char vms_mail_search_unflagged (stream,msgno,d,n)
  1847.     MAILSTREAM *stream;
  1848.     long msgno;
  1849.     char *d;
  1850.     long n;
  1851. {
  1852.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1853. }
  1854.  
  1855.  
  1856. char vms_mail_search_unkeyword (stream,msgno,d,n)
  1857.     MAILSTREAM *stream;
  1858.     long msgno;
  1859.     char *d;
  1860.     long n;
  1861. {
  1862.   return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
  1863. }
  1864.  
  1865.  
  1866. char vms_mail_search_unseen (stream,msgno,d,n)
  1867.     MAILSTREAM *stream;
  1868.     long msgno;
  1869.     char *d;
  1870.     long n;
  1871. {
  1872.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1873. }
  1874.  
  1875. char vms_mail_search_before (stream,msgno,d,n)
  1876.     MAILSTREAM *stream;
  1877.     long msgno;
  1878.     char *d;
  1879.     long n;
  1880. {
  1881.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1882.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1883. }
  1884.  
  1885.  
  1886. char vms_mail_search_on (stream,msgno,d,n)
  1887.     MAILSTREAM *stream;
  1888.     long msgno;
  1889.     char *d;
  1890.     long n;
  1891. {
  1892.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1893.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1894. }
  1895.  
  1896.  
  1897. char vms_mail_search_since (stream,msgno,d,n)
  1898.     MAILSTREAM *stream;
  1899.     long msgno;
  1900.     char *d;
  1901.     long n;
  1902. {
  1903.                 /* everybody interprets "since" as .GE. */
  1904.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1905.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1906. }
  1907.  
  1908. char vms_mail_search_body (stream,msgno,d,n)
  1909.     MAILSTREAM *stream;
  1910.     long msgno;
  1911.     char *d;
  1912.     long n;
  1913. {
  1914.   char ret;
  1915.   char *s = vms_mail_fetchtext (stream,msgno);    /* get the text in LOCAL->buf */
  1916.  
  1917.   ret = search (s,strlen(s),d,n);/* do the search */
  1918.   return ret;            /* return search value */
  1919. }
  1920.  
  1921. char vms_mail_search_subject (stream,msgno,d,n)
  1922.     MAILSTREAM *stream;
  1923.     long msgno;
  1924.     char *d;
  1925.     long n;
  1926. {
  1927.   char *s = vms_mail_fetchstructure (stream,msgno,NIL)->subject;
  1928.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1929. }
  1930.  
  1931.  
  1932. char vms_mail_search_text (stream,msgno,d,n)
  1933.     MAILSTREAM *stream;
  1934.     long msgno;
  1935.     char *d;
  1936.     long n;
  1937. {
  1938.   char ret;
  1939.   char *s = vms_mail_fetchheader (stream,msgno);    /* Get the header in LOCAL->buf */
  1940.  
  1941.   ret = search (s,strlen(s),d,n);  /* First, search at the header */
  1942.   if(!ret)    /* Not found at header, search at the body */
  1943.       ret = vms_mail_search_body (stream,msgno,d,n);
  1944.   return ret;            /* return search value */
  1945. }
  1946.  
  1947. char vms_mail_search_bcc (stream,msgno,d,n)
  1948.     MAILSTREAM *stream;
  1949.     long msgno;
  1950.     char *d;
  1951.     long n;
  1952. {
  1953.   ADDRESS *a = vms_mail_fetchstructure (stream,msgno,NIL)->bcc;
  1954.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1955.                 /* get text for address */
  1956.   rfc822_write_address (LOCAL->buf,a);
  1957.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1958. }
  1959.  
  1960.  
  1961. char vms_mail_search_cc (stream,msgno,d,n)
  1962.     MAILSTREAM *stream;
  1963.     long msgno;
  1964.     char *d;
  1965.     long n;
  1966. {
  1967.   ADDRESS *a = vms_mail_fetchstructure (stream,msgno,NIL)->cc;
  1968.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1969.                 /* get text for address */
  1970.   rfc822_write_address (LOCAL->buf,a);
  1971.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1972. }
  1973.  
  1974.  
  1975. char vms_mail_search_from (stream,msgno,d,n)
  1976.     MAILSTREAM *stream;
  1977.     long msgno;
  1978.     char *d;
  1979.     long n;
  1980. {
  1981.   ADDRESS *a = vms_mail_fetchstructure (stream,msgno,NIL)->from;
  1982.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1983.                 /* get text for address */
  1984.   rfc822_write_address (LOCAL->buf,a);
  1985.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1986. }
  1987.  
  1988.  
  1989. char vms_mail_search_to (stream,msgno,d,n)
  1990.     MAILSTREAM *stream;
  1991.     long msgno;
  1992.     char *d;
  1993.     long n;
  1994. {
  1995.   ADDRESS *a = vms_mail_fetchstructure (stream,msgno,NIL)->to;
  1996.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1997.                 /* get text for address */
  1998.   rfc822_write_address (LOCAL->buf,a);
  1999.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  2000. }
  2001.  
  2002. /* Search parsers */
  2003.  
  2004.  
  2005. /* Parse a date
  2006.  * Accepts: function to return
  2007.  *        pointer to date integer to return
  2008.  * Returns: function to return
  2009.  */
  2010.  
  2011. search_t vms_mail_search_date (f,n)
  2012.     search_t f;
  2013.     long *n;
  2014. {
  2015.   long i;
  2016.   char *s;
  2017.   MESSAGECACHE elt;
  2018.                 /* parse the date and return fn if OK */
  2019.   return (vms_mail_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  2020.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  2021. }
  2022.  
  2023. /* Parse a flag
  2024.  * Accepts: function to return
  2025.  *        pointer to keyword integer to return
  2026.  *        MAIL stream
  2027.  * Returns: function to return
  2028.  */
  2029.  
  2030. search_t vms_mail_search_flag (f,n,stream)
  2031.     search_t f;
  2032.     long *n;
  2033.     MAILSTREAM *stream;
  2034. {
  2035.   short i;
  2036.   char *s,*t;
  2037.   if (t = strtok (NIL," ")) {    /* get a keyword */
  2038.     ucase (t);            /* get uppercase form of flag */
  2039.     for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
  2040.       if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
  2041.   }
  2042.   return NIL;            /* couldn't find keyword */
  2043. }
  2044.  
  2045. /* Parse a string
  2046.  * Accepts: function to return
  2047.  *        pointer to string to return
  2048.  *        pointer to string length to return
  2049.  * Returns: function to return
  2050.  */
  2051.  
  2052.  
  2053. search_t vms_mail_search_string (f,d,n)
  2054.     search_t f;
  2055.     char **d;
  2056.     long *n;
  2057. {
  2058.   char *end = " ";
  2059.   char *c = strtok (NIL,"");    /* remainder of criteria */
  2060.   if (!c) return NIL;        /* missing argument */
  2061.   switch (*c) {            /* see what the argument is */
  2062.   case '{':            /* literal string */
  2063.     *n = strtol (c+1,d,10);    /* get its length */
  2064.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  2065.     (!(*(c = *d + *n)) || (*c == ' '))) {
  2066.       char e = *--c;
  2067.       *c = DELIM;        /* make sure not a space */
  2068.       strtok (c," ");        /* reset the strtok mechanism */
  2069.       *c = e;            /* put character back */
  2070.       break;
  2071.     }
  2072.   case '\0':            /* catch bogons */
  2073.   case ' ':
  2074.     return NIL;
  2075.   case '"':            /* quoted string */
  2076.     if (strchr (c+1,'"')) end = "\"";
  2077.     else return NIL;
  2078.   default:            /* atomic string */
  2079.     if (*d = strtok (c,end)) *n = strlen (*d);
  2080.     else return NIL;
  2081.     break;
  2082.   }
  2083.   return f;
  2084. }
  2085.  
  2086. /*
  2087.  | Copy a message from the current folder to another one. Do not mark the
  2088.  | original message for deletion as we do not have the Stream available...
  2089.  | Furthermore, the caller will set the D flag for it.
  2090.  | VMS/MAIL strangeness: If the user set its mail directory to something else
  2091.  | than the login directory, the message is moved to the login directory
  2092.  | unless we explicitly specify the mail directory (which we do...).
  2093.  | Returns: 0 - some error.
  2094.  |          1 - success.
  2095.  */
  2096. int
  2097. vms_move_folder(folder, context, message)
  2098.      char         *folder;
  2099.      void    *context;
  2100.      MESSAGECACHE *message;
  2101. {
  2102.     static int    MessageNum;    /* Message number to move */
  2103.     static char    FolderName[256];    /* Hold here the folder name */
  2104.     int    status;
  2105.     struct    ITEM_LIST NewFolderItem[] = {    /* get a list of all folders */
  2106. /* Must be first*/ { 0, MAIL$_MESSAGE_FOLDER, FolderName, NULL},     /* New folder name */
  2107. /* Must be second*/ { 0, MAIL$_MESSAGE_FILENAME, GlobalMailDirectory, NULL },
  2108.         { sizeof(MessageNum), MAIL$_MESSAGE_ID, &MessageNum, NULL}, /* Message number to move */
  2109.         { 0, 0, NULL, NULL } };
  2110.     static int    FolderCreated;    /* Boolean reply code */
  2111.     struct    ITEM_LIST FolderCreatedItem[] = { /* Was a new folder created? */
  2112.         { sizeof(FolderCreated), MAIL$_MESSAGE_FOLDER_CREATED,
  2113.             &FolderCreated, NULL },
  2114.         { 0, 0, NULL, NULL }};
  2115.  
  2116.     ucase(strcpy(FolderName, folder));    /* Make it upper case */
  2117.     NewFolderItem[0].length = strlen(FolderName);
  2118.     NewFolderItem[1].length = strlen(GlobalMailDirectory);
  2119.     MessageNum = message->msgno;
  2120.     status = mail$message_copy(&MessageContext, NewFolderItem, FolderCreatedItem);
  2121.     if((status & 0x1) == 0) {
  2122.         mm_log("Can't file message", NIL);
  2123.         return 0;
  2124.     }
  2125.     return 1;    /* All ok */
  2126. }
  2127.  
  2128.  
  2129. /*
  2130.  | If the folder is NEWMAIL then file all read and undeleted messages in
  2131.  | MAIL folder.
  2132.  */
  2133. vms_file_newmail_messages(stream)
  2134. MAILSTREAM *stream;
  2135. {
  2136.     int    i;
  2137.     MESSAGECACHE    message;    /* File message uses this... */
  2138.     char    sequence[256];    /* SetFlags uses strings and not integers... */
  2139.  
  2140. /* Check whether we have something to do */
  2141.     if(strcmp(stream->mailbox, "NEWMAIL") != 0)
  2142.         return;    /* Not NEWMAIL - ignore */
  2143.  
  2144. /* loop over all messages and check which are marked as SEEN and not as
  2145.    DELETED.
  2146. */
  2147.     for(i = 1; i <= stream->nmsgs; i++) {
  2148.         if(MessageTextCache[i - 1].flags & fSEEN) {    /* Message seen */
  2149.             if((MessageTextCache[i - 1].flags & fDELETED) != fDELETED) { /* And not deleted */
  2150.                 message.msgno = i;
  2151.                 vms_move_folder("MAIL", NULL, &message);
  2152.                 sprintf(sequence, "%d", i);
  2153.                 vms_mail_setflag (stream,sequence,"(\\DELETED)");    /* Delete it from NEWMAIL */
  2154.             }
  2155.         }
  2156.     }
  2157.  
  2158. /* Now remove all deleted messages from NEWMAIL. We do not leave it to the close
  2159.    routine since the user might answer there with N and then we have two copies
  2160.    of the message: one in NEWMAIL and one in MAIL.
  2161. */
  2162.     vms_mail_expunge (stream);
  2163. }
  2164.  
  2165.  
  2166. /*
  2167.  | Get the message's text filename, the addresses and send it using the
  2168.  | selected mail protocol.
  2169.  | Returns NULL in case of success, or a pointer to the error message.
  2170.  */
  2171. static int MessageSentCount;    /* Set by the error/success routine */
  2172. char *
  2173. vms_mail_send(filename, from, to, cc, subject)
  2174. char    *filename;
  2175. ADDRESS    *from, *to, *cc;
  2176. char    *subject;
  2177. {
  2178.     ADDRESS    *ap;
  2179.     static unsigned int    SendContext;
  2180.     int    status, AddressesCount, success_send_result();
  2181.     FILE    *ifd;
  2182.     static char line[256];    /* used also to return error messages to caller */
  2183.     char    *p, MailProtocolUsed[256];    /* The prefix name */
  2184.  
  2185.     struct    ITEM_LIST NullItemList[] = {{ 0, 0, NULL, NULL }};    /* Null list */
  2186.     struct    ITEM_LIST ToItem[] = {
  2187.         { 0, MAIL$_SEND_USERNAME, line, NULL },
  2188.         { 0, 0, NULL, NULL }};
  2189.     struct    ITEM_LIST    FromItem[] = {
  2190.         { 0, MAIL$_SEND_FROM_LINE, line, NULL },
  2191.         { 0, 0, NULL, NULL }};
  2192.     struct    ITEM_LIST    SubjectItem[] = {
  2193.         { 0, MAIL$_SEND_SUBJECT, line, NULL },
  2194.         { 0, 0, NULL, NULL }};
  2195.     struct    ITEM_LIST    RecordItem[] = {
  2196.         { 0, MAIL$_SEND_RECORD, line, NULL },
  2197.         { 0, 0, NULL, NULL }};
  2198.     struct    ITEM_LIST    SendItem[] = {
  2199.         { sizeof (int *), MAIL$_SEND_SUCCESS_ENTRY, success_send_result, NULL },
  2200.         { 0, 0, NULL, NULL }};
  2201.  
  2202. /* Get he foreign protocol to use */
  2203.     if((p = getenv("PINE_MAIL_PROTOCOL")) != NULL)
  2204.         strcpy(MailProtocolUsed, p);
  2205.     else {    /* No foreign protocol - can't work */
  2206.         sprintf(line, "No foreign mail transport protocol defined");
  2207.         return line;
  2208.     }
  2209.  
  2210. /* Create mail context */
  2211.     SendContext = 0;
  2212.     status = mail$send_begin(&SendContext, NullItemList, NullItemList);
  2213.     if((status & 0x1) == 0) {
  2214.         mail$send_end(&SendContext, NullItemList, NullItemList);
  2215.         sprintf(line, "Send-message Begin failed, status=%d", status);
  2216.         return line;
  2217.     }
  2218.  
  2219. /* Create the sender's address */
  2220.     sprintf(line, "%s <%s@%s>", (from->personal ? from->personal : ""),
  2221.         from->mailbox, from->host);
  2222.     FromItem[0].length = strlen(line);
  2223.     status = mail$send_add_attribute(&SendContext, FromItem, NullItemList);
  2224.     if((status & 0x1) == 0) {
  2225.         mail$send_end(&SendContext, NullItemList, NullItemList);
  2226.         sprintf(line, "SEND-message Add attribute failed, status=%d", status);
  2227.         return line;
  2228.     }
  2229.  
  2230. /* Now the subject */
  2231.     if(subject) {
  2232.         strcpy(line, subject);
  2233.         SubjectItem[0].length = strlen(line);
  2234.         status = mail$send_add_attribute(&SendContext, SubjectItem, NullItemList);
  2235.         if((status & 0x1) == 0) {
  2236.             mail$send_end(&SendContext, NullItemList, NullItemList);
  2237.             sprintf(line, "send-message: Add attribute failed, status=%d", status);
  2238.             return line;
  2239.         }
  2240.     }
  2241.  
  2242. /* now add the TO addresses */
  2243.     AddressesCount = 0;
  2244.     ap = to;
  2245.     while(ap != NULL) {
  2246.         sprintf(line, "%s%%\"%s@%s\"", MailProtocolUsed, ap->mailbox, ap->host);
  2247.         ToItem[0].length = strlen(line);
  2248.         status = mail$send_add_address(&SendContext, ToItem, NullItemList);
  2249.         if((status & 0x1) == 0) {
  2250.             mail$send_end(&SendContext, NullItemList, NullItemList);
  2251.             sprintf(line, "Send-message: Add address failed, status=%d", status);
  2252.             return line;
  2253.         }
  2254.         ap = ap->next;
  2255.         AddressesCount++;
  2256.     }
  2257.  
  2258. /* Same for CC */
  2259.     ap = cc;
  2260.     while(ap != NULL) {
  2261.         sprintf(line, "%s%%\"%s@%s\"", MailProtocolUsed, ap->mailbox, ap->host);
  2262.         ToItem[0].length = strlen(line);
  2263.         status = mail$send_add_address(&SendContext, ToItem, NullItemList);
  2264.         if((status & 0x1) == 0) {
  2265.             mail$send_end(&SendContext, NullItemList, NullItemList);
  2266.             sprintf(line, "Send-message: Add address failed, status=%d", status);
  2267.             return line;
  2268.         }
  2269.         ap = ap->next;
  2270.         AddressesCount++;
  2271.     }
  2272.  
  2273. /* Open the message's file */
  2274.     if((ifd = fopen(filename, "r")) == NULL) {
  2275.         mail$send_end(&SendContext, NullItemList, NullItemList);
  2276.         sprintf(line, "Can't open '%s'", filename);
  2277.         return line;
  2278.     }
  2279.  
  2280. /* Now copy it. VMS/MAIL can accept up to 255 characters long lines */
  2281.     while(fgets(line, (sizeof(line) < 255 ? sizeof(line) - 1 : 255), ifd) != NULL) {
  2282.         char *p = strchr(line, '\n');
  2283.         if(p) *p = '\0';    /* Remove the NL */
  2284.         RecordItem[0].length = strlen(line);
  2285.  
  2286.         status = mail$send_add_bodypart(&SendContext, RecordItem, NullItemList);
  2287.         if((status & 0x1) == 0) {
  2288.             mail$send_end(&SendContext, NullItemList, NullItemList);
  2289.             sprintf(line, "Send-message: Add Body failed, status=%d", status);
  2290.             return line;
  2291.         }
  2292.     }
  2293.     fclose(ifd);
  2294.  
  2295. /* OK, done - actually send the message. This routine will call via AST a routine
  2296.    that will count how many were sent ok.
  2297. */
  2298.     MessageSentCount = 0;    /* None set yet */
  2299.     status = mail$send_message(&SendContext, SendItem, NullItemList);
  2300.     if((status & 0x1) == 0) {
  2301.         mail$send_end(&SendContext, NullItemList, NullItemList);
  2302.         sprintf(line, "send-message: Message send failed, status=%d", status);
  2303.         return line;
  2304.     }
  2305.  
  2306.     status = mail$send_end(&SendContext, NullItemList, NullItemList);
  2307.  
  2308.     if(MessageSentCount < AddressesCount) {
  2309.         sprintf(line, "Error at final stages of delivery");
  2310.         return line;
  2311.     }
  2312.  
  2313.     return NULL;    /* Success */
  2314. }
  2315.  
  2316.  
  2317. /*
  2318.  | this routine is called only if the message was sent ok. We count the number
  2319.  | of messages sent ok.
  2320.  */
  2321. success_send_result()
  2322. {
  2323.     MessageSentCount++;    /* OK */
  2324. }
  2325.